<?php
/**
 * Created by PhpStorm.
 * User: 异步加载
 * Date: 2017/12/20
 * Time: 20:18
 */

namespace app\api\service;


use app\api\model\User;
use app\lib\enum\ScopeEnum;
use app\lib\exception\TokenException;
use app\lib\exception\WeChatException;

class UserToken extends Token
{
    protected $code;
    protected $wxLoginUrl;
    protected $wxAppID;
    protected $wxAppSecret;

    function __construct($code)
    {
        $this->code = $code;
        $this->wxAppID = config('wx.app_id');
        $this->wxAppSecret = config('wx.app_secret');
        $this->wxLoginUrl = sprintf(
            config('wx.login_url'), $this->wxAppID, $this->wxAppSecret, $this->code);
    }

    /**
     * 获取openid并下发令牌
     */
    public function get(){
        $result = curl_get($this->wxLoginUrl);

         //注意json_decode的第一个参数true
         //这将使字符串被转化为数组而非对象

        $wxResult = json_decode($result, true);
        if (empty($wxResult)) {
             //为什么以empty判断是否错误，这是根据微信返回
             //规则摸索出来的
             //这种情况通常是由于传入不合法的code
            throw new Exception('获取session_key及openID时异常，微信内部错误');
        }else {
             //建议用明确的变量来表示是否成功
             //微信服务器并不会将错误标记为400，无论成功还是失败都标记成200
             //这样非常不好判断，只能使用errcode是否存在来判断
            $loginFail = array_key_exists('errcode', $wxResult);
            if ($loginFail) {
                $this->processLoginError($wxResult);
            }
            else {
                return $this->grantToken($wxResult);
            }
        }
    }

    /**
     * @param $wxResult
     * @throws WeChatException
     * 处理微信登陆异常那些异常应该返回客户端，那些异常不应该返回客户端需要认真思考
     */
    private function processLoginError($wxResult){
        throw new WeChatException([
            'msg' => $wxResult['errmsg'],
            'errorCode' => $wxResult['errcode']
        ]);
    }

    /**
     * @param $wxResult
     * @return string
     * 令牌下发
     */
    private function grantToken($wxResult){
        $openid = $wxResult['openid'];
        $user = User::getByopenID($openid);     //数据库中检索openid是否存在
        if($user){
            $uid = $user->id;
        }else{
            $uid = $this->newUser($openid);    //不存在则新建用户
        }
        $cachedValue = $this->prepareCachedValue($wxResult,$uid); //得到令牌
        $token = $this->saveToCache($cachedValue); //将令牌存入缓存
        return $token;
    }

    /**
     * @param $openid
     * @return mixed
     * 创建一个新用户func
     */
    private function newUser($openid){
        $user = User::create([
            'openid' => $openid
        ]);
        return $user->id;
    }

    /**
     * @param $wxResult
     * @param $uid
     * @return mixed
     * 拼接value func
     */
    private function prepareCachedValue($wxResult,$uid){
        $cachedValue = $wxResult;
        $cachedValue['uid'] = $uid;
        //根据scope来判断权限,
        $cachedValue['scope'] = ScopeEnum::User;
        return $cachedValue;
    }

    /**
     * @param $cachedValue
     * @return string
     * @throws TokenException
     * 将令牌写入TP5缓存func
     */
    private function saveToCache($cachedValue){
        $key = self::generateToken();
        $value = json_decode($cachedValue);     //  将数组转化成字符串
        $expire_in = config('setting.token_expire_in');
        $request = cache($key,$value,$expire_in); //将令牌存入TP5自带缓存
        if(!$request){
            throw new TokenException([
                'msg' => '服务器缓存异常',
                'errorCode' => 10005
            ]);
        }
        return $key;
    }
}